package com.lin.eventbus.internal;

import com.lin.eventbus.exception.InternalEventChannelNotFoundException
import com.lin.eventbus.exception.InternalEventListenerNotFoundException
import com.lmax.disruptor.WorkHandler

class InternalEventBusEventHandler private constructor() : WorkHandler<BusEvent> {

    companion object {
        @JvmField
        val INSTANCE = InternalEventBusEventHandler()
    }

    override fun onEvent(event: BusEvent) {
        event.receivedCount?.increment()
        try {
            processEventBusBossEvent(event)
        } finally {
            event.clear()
        }
    }

    private fun processEventBusBossEvent(event: BusEvent) {
        val ctx = event.ctx!!
        val worker = InternalInternalEventBus.INSTANCE.lookup(ctx.channel)
        if (worker == null) {
            event.workerMissedCount?.increment()
            ctx.bossExceptionCaught(InternalEventChannelNotFoundException())
            ctx.complete()
            return
        }
        val listeners = worker.lookup(ctx)
        if (listeners.isEmpty()) {
            event.listenerMissedCount?.increment()
            ctx.bossExceptionCaught(InternalEventListenerNotFoundException())
            ctx.complete()
            return
        }
        if (ctx.event.isFair) {
            val sorted = listeners.sortedWith(Comparator { o1, o2 ->
                com.lin.eventbus.util.ObjectUtils.calculateOrder(o1).compareTo(com.lin.eventbus.util.ObjectUtils.calculateOrder(o2))
            }).toList()
            ctx.initializeTask(1)
            worker.publish(0, ctx, sorted)
            event.dispatchedCount?.increment()
        } else {
            ctx.initializeTask(listeners.size)
            listeners.forEachIndexed { taskId, listener ->
                worker.publish(taskId, ctx, listOf(listener))
                event.dispatchedCount?.increment()
            }
        }
    }
}